package com.gp.base;


import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import com.gp.base.except.BusinessException;
import com.gp.base.util.ECPage;
import com.gp.base.util.GenericsUtils;
import com.gp.base.util.SpringContextUtil;
import com.opensymphony.xwork2.ActionSupport;

/**
 * 
 * <li>类型名称：
 * <li>说明：普通的功能（即普通的增删改查功能）的action继承于此类，子类必须提供action操作的实体类型T，查询使用
 * <li>的SerachBean S和action所依赖的manager对象；此类只提供对单个业务实体的增删改查的默认实现。
 * <li>创建人： 曾明辉
 * <li>创建日期：2011-03-04
 * <li>修改人： 
 * <li>修改日期：
 */
public abstract class BaseAction <T,M extends BaseManager<T>> extends ActionSupport{
	
	protected static final String LIST = "list";
	protected static final String EDIT = "edit";
	protected static final String VIEW = "view";
			
    protected static final String MSG_DELETE_SUCCESS = "【删除】成功！";
    protected static final String MSG_INSERT_SUCCESS = "【新增】成功！";
    protected static final String MSG_UPDATE_SUCCESS = "【修改】成功！";
	
	protected M baseManager;         // action 所依赖的服务对象
	
	protected String id;             // 实体的id,主要用于根据id获取实体
    protected String[] ids;          //用于保存多个业务实体的ID数组    
    protected List<T> dataList;      //查询结果列表
	protected T entity;              //action 所操作的主导业务实体对象
	protected T entitySearch;        // 用于保存传递查询的JavaBean	
	
	protected ECPage page;           //ECPage查询翻页

	@SuppressWarnings("unused")
	private Logger logger = Logger.getLogger(getClass().getName()); 
    
		/**
	    * 
	    * <li>说明：构造方法，创建entity实体，并获取entity的具体类型；
	    * <li>设置默认的查询，编辑页面名称；并设置方法返回页面影射表的内容。
	    * <li>创建人：曾明辉
	    * <li>创建日期：2011-03-04
	    * <li>修改人： 
	    * <li>修改日期：
	    */
	    @SuppressWarnings("unchecked")
		public BaseAction(){
	    	try {    		
				this.entity = (T) GenericsUtils.getSuperClassGenricType(getClass()).newInstance();
				this.entitySearch = (T)GenericsUtils.getSuperClassGenricType(getClass()).newInstance();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			String managerName = StringUtils.uncapitalize(GenericsUtils.getSuperClassGenricType(getClass(),1).getSimpleName()); 
			this.baseManager = (M)SpringContextUtil.getBean(managerName);
	    }
	    	
		
	    /**
		 * 
		 * <li>方法名：getEntity
		 * <li>@return 范型指定类型的实体对象。
		 * <li>返回类型：T
		 * <li>说明：获取范型指定类型的实体对象。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public T getEntity() {
			return entity;
		}
		
		/**
		 * 
		 * <li>方法名：setEntity
		 * <li>@param entity 范型指定类型的实体对象。
		 * <li>返回类型：void
		 * <li>说明：设置范型指定类型的实体对象，此方法可供程序员调用；struts框架值设置拦截器在填值的时候会自动调用本方法。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public void setEntity(T entity) {
			this.entity = entity;
		}
		
			
		/**
		 * 
		 * <li>方法名：getEntitySearch
		 * <li>@return 返回范型指定的查询bean对象。
		 * <li>返回类型：S
		 * <li>说明：获取范型指定的查询bean对象。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public T getEntitySearch() {
			return entitySearch;
		}

		/**
		 * 
		 * <li>方法名：setEntitySearch
		 * <li>@param entitySearch 范型指定的查询bean对象。
		 * <li>返回类型：void
		 * <li>说明：设置范型指定的查询bean对象。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public void setEntitySearch(T entitySearch) {
			this.entitySearch = entitySearch;
		}
		
		/**
		 * 
		 * <li>方法名：getId
		 * <li>@return 用于和前台进行数据交换的id变量
		 * <li>返回类型：String
		 * <li>说明：
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String getId() {
			return this.id;
		}
		/**
		 * 
		 * <li>方法名：setId
		 * <li>@param id 用于和前台进行数据交换的id变量
		 * <li>返回类型：void
		 * <li>说明：
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public void setId(String id) {
			this.id = id;
		}
		/**
		 * 
		 * <li>方法名：getIds
		 * <li>@return 用于和前台进行数据交换的ids变量,该变量保存的是id的一个数组。
		 * <li>返回类型：String[]
		 * <li>说明：
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String[] getIds(){
			return this.ids;
		}
		/**
		 * 
		 * <li>方法名：setIds
		 * <li>@param ids 用于和前台进行数据交换的ids变量,该变量保存的是id的一个数组。
		 * <li>返回类型：void
		 * <li>说明：
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public void setIds(String[] ids) {
			this.ids = ids;
		}
		
		/**
		 * 
		 * <li>方法名：getDataList
		 * <li>@return 用于action和页面之间进行查询结果集合传递的变量。
		 * <li>返回类型：Collection
		 * <li>说明：
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public List<T> getDataList() {
			return dataList;
		}
		/**
		 * 
		 * <li>方法名：setDataList
		 * <li>@param dataList 用于action和页面之间进行查询结果集合传递的变量。
		 * <li>返回类型：void
		 * <li>说明：
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public void setDataList(List<T> dataList) {
			this.dataList = dataList;
		}
		
		/**
		 * 
		 * <li>方法名：search
		 * <li>@return 返回查询结果页面。
		 * <li>@throws Exception
		 * <li>返回类型：String
		 * <li>说明：根据用户输入的查询条件进行查询，用户在界面上所输入的所有查询条件
		 * <li>都包装在search bean中；系统获得查询结果后返回查询结果页面。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String search()throws Exception  {	
			
			page = ECPage.getInstance(null, getRequest());
			
			this.dataList = this.baseManager.findPage(entitySearch, page, true);	
			
			return LIST;
		}
		
		/**
		 * 
		 * <li>方法名：toAdd
		 * <li>@return 导航到实体新增页面。
		 * <li>@throws Exception
		 * <li>返回类型：String
		 * <li>说明：用户在页面请求新增时，调用此方法；如果有特殊的事情要做，请在
		 * <li>子类中覆盖initCreatePage方法；然后系统导航到新增页面。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String toAdd()throws Exception {
			beforeToAdd();//到添加页面前执行的方法前
			
			return EDIT;
		}
		
		/**
		 * 
		 * <li>方法名：toEdit
		 * <li>@return 导航到实体编辑页面。
		 * <li>@throws Exception
		 * <li>返回类型：String
		 * <li>说明：当用户在页面上浏览某业务实体，想查看该业务实体详细信息或修改
		 * <li>该业务实体时，系统根据该业务实体的id从数据库获取该实体；对修改
		 * <li>JSP页面做必要的初始化工作后，导航到修改页面。如果从数据库中获取
		 * <li>实体之前还需要做别的事情，请在子类中覆盖initEditPage方法。然后系统导航到编辑页面。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String toEdit()throws Exception {		
			entity = this.baseManager.get(id);
			beforeToEdit();//到修改页面前执行的方法前
			return EDIT;
		}
		
		/**
		 * 
		 * <li>方法名：toView
		 * <li>@return
		 * <li>@throws Exception
		 * <li>返回类型：String
		 * <li>说明：
		 * <li>创建人：曾明辉
		 * <li>创建日期：2008-11-5
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String toView() throws Exception{		
			entity = this.baseManager.get(id); 
			return VIEW;
		}

		/**
		 * 
		 * <li>方法名：save
		 * <li>@return 返回修改页面。
		 * <li>@throws Exception
		 * <li>返回类型：String
		 * <li>说明：用户保存数据后，页面返回修改页面，用户可以继续修改
		 * <li>直到离开这个页面为止
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		@SuppressWarnings("unchecked")
		public String save()throws Exception  {
			
			this.saveEntity("edit");//新增或修改对象
			
			return EDIT;
		}
		
		/**
		 * 
		 * <li>方法名：saveToList
		 * <li>@return 返回查询结果页面。
		 * <li>@throws Exception
		 * <li>返回类型：String
		 * <li>说明：用户在页面修改或新增实体保存后后，页面返回在查询结果页面。此方法目前不推荐使用，框架中先保留此方法。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String saveToList()throws Exception  {

			this.saveEntity("list");//新增或修改对象
			
			return LIST;
		}	

		/**
		 * 
		 * <li>方法名：deleteById
		 * <li>@return 返回查询结果页面。
		 * <li>返回类型：String
		 * <li>说明：用ajax根据指定的单个id删除指定的业务实体，删除后返回查询结果页面。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String deleteById() throws Exception{
			
			try{
				this.baseManager.deleteByIds(id);
				this.ajaxMessage(MSG_DELETE_SUCCESS);			
				getRequest().setAttribute("refreshFlag", "refresh");
			}
			catch(BusinessException be){
				this.ajaxMessage(be.getMessage());
			}
			return null;
		}

		/**
		 * 
		 * <li>方法名：deleteByIds
		 * <li>@return 返回查询结果页面。
		 * <li>返回类型：String.
		 * <li>说明：用ajax根据指定的id集合进行批量删除，删除后返回查询结果页面。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public String deleteByIds() throws Exception{
			
			if(ids.length ==1 && ids[0].indexOf(",") != -1){
				ids = ids[0].split(",");
			}
			try{
				this.baseManager.deleteByIds(ids);
				
				this.ajaxMessage(MSG_DELETE_SUCCESS);
				
				getRequest().setAttribute("refreshFlag", "refresh");
				
			}catch(BusinessException be){
				this.ajaxMessage(be.getMessage());
			}
			
			return null;
		}
			
		
		
		 /**
	    * 
	    * <li>方法名：getRequest
	    * <li>@return 返回request对象
	    * <li>返回类型：HttpServletRequest
	    * <li>说明：如果程序员中的程序需要访问request对象，本基类通过本方法提供
	    * <li>创建人：曾明辉
	    * <li>创建日期：2011-03-04
	    * <li>修改人： 
	    * <li>修改日期：
	    */
		public HttpServletRequest getRequest(){	
			return ServletActionContext.getRequest();
		}
		    
	    /**
	     * 
	     * <li>方法名：getResponse
	     * <li>@return 返回response对象
	     * <li>返回类型：HttpServletResponse
	     * <li>说明：如果程序员中的程序需要访问response对象，本基类通过本方法提供
	     * <li>创建人：曾明辉
	     * <li>创建日期：2011-03-04
	     * <li>修改人： 
	     * <li>修改日期：
	     */
		public HttpServletResponse getResponse(){
			return ServletActionContext.getResponse();
		}
		
		/**
		 * 
		 * <li>方法名：getSession
		 * <li>@return 返回session对象
		 * <li>返回类型：HttpSession
		 * <li>说明：如果程序员中的程序需要访问session对象, 本基类通过本方法提供
		 * <li>创建人：曾明辉
		 * <li>创建日期：2011-03-04
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public HttpSession getSession(){
			return getRequest().getSession();
		}

		/**
		 * 
		 * <li>方法名：getServletContext
		 * <li>@return 返回ServletContext对象
		 * <li>返回类型：ServletContext
		 * <li>说明：如果程序员中的程序需要访问ServletContext对象, 本基类通过本方法提供
		 * <li>创建人：曾明辉
		 * <li>创建日期：2009-9-27
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public ServletContext getServletContext(){
			return ServletActionContext.getServletContext();
		}
		
		/**
		 * 
		 * <li>方法名：isAdd
		 * <li>@param entity
		 * <li>@return
		 * <li>返回类型：boolean
		 * <li>说明：通过对象是否有id判断是否是修改对象
		 * <li>创建人：曾明辉
		 * <li>创建日期：2008-11-22
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public boolean isUpdate(Object entity){
			boolean isUpdate = true;
			if(entity == null){
				return false;
			}
			BeanWrapper bw = new BeanWrapperImpl(entity);
			Object id = bw.getPropertyValue("id");
			if(id == null || "".equals(id.toString().trim())){//没有id
				isUpdate = false;
			}
			else{
				isUpdate = true;			
			}
			
			return isUpdate;
		}
			
		/**
		 * 
		 * <li>方法名：fillCollectionField
		 * <li>@param entity 需要操作的对象 
		 * <li>@param fieldName 指需要操作的对象的某一属性名称
		 * <li>@param paramName 提交的参数名称，需要赋值的参数的名称
		 * <li>@throws Exception
		 * <li>返回类型：void
		 * <li>说明:本方法将用逗号分隔的提交数据自动做为对象entity的fieldName属性的范型参数类的对象id属性
		 * <li>    主要用于多对多关系的处理，比如一个用户需要多个角色，提交user.role.id参数是以逗号间隔的角色id，
		 * <li>    User类中有一个roleSet属性，有一个user对象需要进行填充则如下调用：
		 * <li>    fillCollectionField(user,"roleSet","user.role.id")
		 * <li>创建人：曾明辉
		 * <li>创建日期：2008-11-5
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		@SuppressWarnings("unchecked")
		protected Object fillCollectionField(Object entity,String fieldName, String paramName) throws Exception {
			String[] strParams = getRequest().getParameterValues(paramName);
			if(strParams == null || strParams.length == 0){//没有填充的数据
				return entity;
			}
			
			List<String> listId = new ArrayList<String>();	
			for (String param : strParams) {//处理数据
				if("".endsWith(param)){
					continue;
				}
				else{
					String[] ids = param.split(",");//解析逗号分隔的参数
					for (String id : ids) {
						if(! id.equals("")){
							listId.add(id);
						}
					}
				}
			}	
			
			if(listId.size() == 0){//没有数据
				return entity;
			}
			
			String[] allIds = new String[listId.size()];
			allIds = listId.toArray(allIds);
					
			Field field = entity.getClass().getDeclaredField(fieldName);//获得属性
			Type fieldType = field.getGenericType();
			
			if (fieldType instanceof ParameterizedType) {
				ParameterizedType pType = (ParameterizedType) fieldType;
				Type[] types = pType.getActualTypeArguments();//获得参数类型
				if(types.length > 0){
					String className = types[0].toString().substring(
							types[0].toString().indexOf(" "),
							types[0].toString().length()).trim();//前面是Class，后面是类名称，中间用空格间隔
					Class cls = Class.forName(className);

					List<Object> fillObjList = new ArrayList<Object>();
					BaseDao daoUtils = (BaseDao)SpringContextUtil.getBean("baseDao");
					fillObjList = daoUtils.find(cls, allIds);

					BeanWrapper bw = new BeanWrapperImpl(entity);
					Class collCls = bw.getPropertyType(fieldName);
					if(collCls.getName() .equals(Set.class.getName())){//是Set属性
						Set<Object> fillObjSet = new TreeSet<Object>();
						fillObjSet.addAll(fillObjList);

						bw.setPropertyValue(fieldName, fillObjSet);
					}
					else{
						bw.setPropertyValue(fieldName, fillObjList);
					}
				}
			}
			return entity;
		}
		
		/**
		 * 
		 * <li>方法名：ajaxMessage
		 * <li>@param message
		 * <li>@throws IOException
		 * <li>返回类型：void
		 * <li>说明：添加ajax消息处理方法；使用ajax请求，向客户端发送消息。
		 * <li>创建人：曾明辉
		 * <li>创建日期：2008-11-6
		 * <li>修改人： 
		 * <li>修改日期：
		 */
		public void ajaxMessage(String message){
			getResponse().setContentType("text/html; charset=UTF-8");
			PrintWriter out = null;
			try {
				out = getResponse().getWriter();
				out.write(message);
			} catch (IOException e) {
				e.printStackTrace();
			}
			finally{
				out.close();	
			}
		}
		
		/**
		 * 保存对象,处理了业务逻辑的异常
		 * @param returnPage 如为"edit"则要执行beforeToAdd()或beforeToEdit()方法
		 */
		protected void saveEntity(String returnPage) throws Exception{
			try{
				if(isUpdate(entity) ){
					entity = this.baseManager.update(entity);
					if("edit".equals(returnPage)){
						beforeToEdit();//修改后执行到修改页面前执行的方法
					}
					this.addActionMessage(MSG_UPDATE_SUCCESS);
				}else{
					this.baseManager.insert(entity);
					entity = (T) GenericsUtils.getSuperClassGenricType(getClass()).newInstance();
					if("edit".equals(returnPage)){
						beforeToAdd();//添加后执行到添加页面前执行的方法
					}
					
					this.addActionMessage(MSG_INSERT_SUCCESS);
				}
	
				getRequest().setAttribute("refreshFlag", "refresh");
			}catch (BusinessException be) {
				this.addActionError(be.getMessage());
			}
			catch(Exception e){
				throw e;
			}
		}
		
	
	
		
	/**
	 * 到添加页面前执行的方法
	 */
	protected void beforeToAdd(){
		
	}
	
	/**
	 * 到修改页面前执行的方法前
	 */
	protected void beforeToEdit(){
		
	}
}

